import fs from '../fs'
import path from '../../node/path'
import errorCode from '../../node/error-code'
import { checkError } from '../../universalify'

function rimraf(path, callback) {
    let retries = 0;

    _rimraf(path, function CB(err) {
        if (checkError(err)) {
            if (err.message === errorCode.errormessage.EBUSY ||
            err.message === errorCode.errormessage.ENOTEMPTY ||
            err.message === errorCode.errormessage.EPERM && retries < 3) {

                retries++;
                const time = retries * 100;
                //try again, witch the same exact callback as this one.
                return setTimeout(() => _rimraf(path, CB), time)
            }

            // The file is already gone.
            if (err.message === errorCode.errormessage.ENOENT)
                err = null;
        }

        callback(err);
    });
}


function _rimraf(path, callback) {
    fs.lstat(path, (err, stats) => {
        if (checkError(err) && err.message === errorCode.errormessage.ENOENT) {
            return callback(null);
        }

        // Windows can EPERM on stat.
        if (stats && stats.isDirectory()) {
            return _rmdir(path, err, callback);
        }

        fs.unlink(path, (err) => {
            if (checkError(err)) {
                if (err.message === errorCode.errormessage.ENOENT)
                    return callback(null);
                if (err.message === errorCode.errormessage.EPERM)
                    return _rmdir(path, err, callback);
                if (err.message === errorCode.errormessage.EISDIR) {
                    return _rmdir(path, err, callback);
                }
            }
            return callback(err)
        })
    })
}


function _rmdir(path, originalErr, callback) {
    fs.rmdir(path, (err) => {
        if (checkError(err) &&
            (err.message === errorCode.errormessage.ENOTEMPTY ||
             err.message === errorCode.errormessage.EEXIST ||
             err.message === errorCode.errormessage.EPERM)) {
            return _rmchildren(path, callback);
        } else if (checkError(err) && err.message === errorCode.errormessage.ENOTDIR) {
            return callback(originalErr);
        }

        callback(err);
    });
}

function _rmchildren(path, callback) {
    fs.readdir(path, (err, files) => {
        if (checkError(err))
            return callback(err);

        let numFiles = files.length;

        if (numFiles === 0)
            return fs.rmdir(path, callback);

        files.forEach(f => {
            rimraf(path.join(path, f), err => {
                if (checkError(err)) return callback(err)
                if (--numFiles === 0) {
                    fs.rmdir(path, callback);
                }
            });
        });
    });
}


function rimrafSync(path) {
    let stats;

    try {
        stats = fs.lstatSync(path)
    } catch (err) {
        if (err.message === errorCode.errormessage.ENOENT)
            return;
    }

    try {
        // SunOS lets the root user unlink directories.
        if (stats && stats.isDirectory) {
            _rmdirSync(path, null)
        } else {
            fs.unlinkSync(path)
        }
    } catch (err) {
        if (err.message === errorCode.errormessage.ENOENT) {
            return;
        } else if (err.message === errorCode.errormessage.EPERM) {
            _rmdirSync(path, err);
        } else if (err.message !== errorCode.errormessage.EISDIR) {
            throw err;
        }
        _rmdirSync(path, err)
    }
}


function _rmdirSync(path, originalErr) {
    try {
        fs.rmdirSync(path);
    } catch (err) {
        if (err.message === errorCode.errormessage.ENOTDIR) {
            throw originalErr;
        } else if (err.message === errorCode.errormessage.ENOTEMPTY ||
        err.message === errorCode.errormessage.EEXIST ||
        err.message === errorCode.errormessage.EPERM) {
            rmkidsSync(path);
        } else if (err.message !== errorCode.errormessage.ENOENT) {
            throw err;
        }
    }
}

function rmkidsSync(path) {
    fs.readdirSync(path).forEach(f => rimrafSync(path.join(path, f)))
    const ret = fs.rmdirSync(path);
    return ret;
}

export default {
    rimraf,
    rimrafSync
}